﻿using GN.Pay.Utils.Reflection;
using GN.Pay.Utils.Reflection.Meta;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace GN.Pay.Core.Values
{
    /// <summary>
    /// 值对象抽象
    /// </summary>
    [Serializable]
    public abstract class AValueObject<TValueObject> : IEquatable<TValueObject>
        where TValueObject : AValueObject<TValueObject>
    {
        /// <summary>
        /// 实例化 AValueObject 类新实例
        /// </summary>
        protected AValueObject()
        {

        }

        /// <summary>
        /// 获取成员元集合
        /// </summary>
        /// <returns></returns>
        public ISet<MetaMember> MetaMembers()
        {
            return ReflectionUtils.GetCacheMetaMembers(this.GetType());
        }

        /// <summary>
        /// 比较两个对象是否相等
        /// </summary>
        /// <param name="other">对象</param>
        /// <returns></returns>
        public virtual bool Equals(TValueObject other)
        {
            if ((object)other == null)
            {
                return false;
            }
            var members = this.MetaMembers();
            if (!members.Any())
            {
                return true;
            }
            return members.All(m => m.GetMemberValue != null && Equals(m.GetMemberValue(this), m.GetMemberValue(other)));
        }

        /// <summary>
        /// 比较两个对象是否相等
        /// </summary>
        /// <param name="obj">指定的对象</param>
        /// <returns></returns>
        public sealed override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }
            var item = obj as AValueObject<TValueObject>;
            return (object)item != null && this.Equals((TValueObject)item);
        }

        /// <summary>
        /// 获取希哈值
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            const int index = 1;
            const int initialHasCode = 31;
            var members = this.MetaMembers();
            if (!members.Any())
            {
                return initialHasCode;
            }
            var hashCode = initialHasCode;
            var changeMultiplier = false;
            foreach (var member in members)
            {
                if (member.GetMemberValue != null)
                {
                    var value = member.GetMemberValue(this);
                    if (value == null)
                    {
                        hashCode = hashCode ^ (index * 13);
                        continue;
                    }
                    hashCode = hashCode * (changeMultiplier ? 59 : 114) + value.GetHashCode();
                    changeMultiplier = !changeMultiplier;
                }
            }
            return hashCode;
        }
    }
}
